﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using E4D.AppFabric.Entities;
using Microsoft.ApplicationServer.Caching.AdminApi;
using E4D.AppFabric.Entities.Cache_Host;
using E4D.AppFabric.Entities.Engine;

namespace MDCAdminTool
{
    public enum Eviction
    {
        None ,
        LRU
    }

    public class MdcCache : PowerShellBase<CacheHost, CacheRegion> 
    {
        #region Fields
        private bool            _IsHighAvailability;
        private Eviction        _Eviction;
        private long            _TTL;
        private bool            _Expirable;
        private bool            _NotificationsEnabled;
        private NamedCacheStats _NamedCacheStats;
        private string          _CacheType;
        //private Collection<CacheRegion>  _CacheRegions = new Collection<CacheRegion>();
        private long _regionCount;
        #endregion

        #region Properties
        public string CacheType
        {
            get { return _CacheType; }
            set { _CacheType = value; OnPropertyChanged( "CacheType" ); }
        }
        public bool IsHighAvailability
        {
            get { return _IsHighAvailability; }
            set { _IsHighAvailability = value; OnPropertyChanged( "IsHighAvailability" ); }
        }
        public Eviction Eviction
        {
            get { return _Eviction; }
            set { _Eviction = value; OnPropertyChanged( "Eviction" ); }
        }
        public long TTL
        {
            get { return _TTL; }
            set { _TTL = value; OnPropertyChanged( "TTL" ); }
        }
        public bool IsExpirable
        {
            get { return _Expirable; }
            set { _Expirable = value; OnPropertyChanged( "Expirable" ); }
        }
        public Dictionary<string , string[]> HostRegionMap { set; get; }
        public NamedCacheStats NamedCacheStats
        {
            get { return _NamedCacheStats; }
            set { _NamedCacheStats = value; OnPropertyChanged( "NamedCacheStats" ); }
        }
        public bool NotificationsEnabled
        {
            get { return _NotificationsEnabled; }
            set { _NotificationsEnabled = value; OnPropertyChanged( "NotificationsEnabled" ); }
        }

        //public Collection<CacheRegion> CacheRegions
        //{
        //    get { return _CacheRegions; }
        //    set
        //    {
        //        if (value == null)
        //        {
        //            value = new Collection<CacheRegion>();
        //        }
        //        _CacheRegions = value;
        //        OnPropertyChanged("CacheRegions");
        //        OnPropertyChanged("Children");
        //    }
        //}

        public long RegionCount
        {
            get { return _regionCount; }
            set
            {
                _regionCount = value;
                this.OnPropertyChanged("RegionCount");
            }
        }
        

        #endregion

        #region Constructors
        public MdcCache( string name )
        {
            Type = MDCAdminTool.CacheType.CacheName;
            Name = name;
        }

        public MdcCache( MdcCache other ) : this(other.Name)
        {
            Parent = other.Parent;
            Params = new Dictionary<string, object>
                        {
                            {"CacheName"  , other.Params["CacheName"]}
                        };

            Output = other.Output;

            _IsHighAvailability = other._IsHighAvailability;
            _Eviction = other._Eviction;
            _TTL = other._TTL;
            _Expirable = other._Expirable;
            _NotificationsEnabled = other._NotificationsEnabled;
            _NamedCacheStats = other._NamedCacheStats;
            _CacheType = other._CacheType;
            //foreach (var region in other._CacheRegions)
            //    _CacheRegions.Add(region);
            _regionCount = other._regionCount;
        }

        public MdcCache( CacheHost host , string name ) : this(name)
        {
            
            //IsHighAvailability  = false;
            //Eviction            = Eviction.LRU;
            //TTL                 = 10;
            //NotExpirable        = true;
            Parent                = host;
            //CacheRegions        = new Collection<CacheRegion>();
            Params     = new Dictionary<string , object>
                        {
                            {"CacheName"  , Name}
                        };

            Output = host.Output;

            using (var session = SessionFactory.getLocalSession())
            {
                Parent.Cluster.UseCacheCluster(session);
                LoadConfig(session);
                LoadRegions(session);
            }
        }
        public MdcCache( CacheHost host , CacheInfo cacheInfo )
            : this( host , cacheInfo.CacheName )
        {

        }
        public MdcCache( CacheHost host , string name , bool notificationsEnabled , bool isHighAvailability , Eviction eviction , Int64 Ttl , bool isExpirable )
            : this( host , name )
        {
            IsHighAvailability  = isHighAvailability;
            Eviction            = eviction;
            TTL                 = Ttl;
            IsExpirable        = !isExpirable;
            NotificationsEnabled = notificationsEnabled;
        }
        #endregion

        #region PowerShell Commands

        /// <summary>
        /// Returns a list of all regions in the cache cluster or only those in a named cache.
        /// </summary>
        /// <returns>Returns a list of all regions in the cache cluster or only those in a named cache.</returns>
        public void LoadRegions()
        {
            using (var session = SessionFactory.getLocalSession())
            {
                Parent.Cluster.UseCacheCluster(session);
                LoadRegions(session);
            }
        }

        protected void LoadRegions(ISession session)
        {
            //var r = Invoke(session, "Get-CacheRegion", Params);
            //if (r == null || r.Count == 0)
            //{
            //    CacheRegions = null;
            //    return;
            //}
            //var result = GetRegionResult(r);
            //CacheRegions = result;

            var r = Invoke(session, "Get-CacheStatistics", Params);
            if (r != null && r.Count > 0 && r[0].Properties["RegionCount"] != null)
                RegionCount = (long)r[0].Properties["RegionCount"].Value;
            else
                RegionCount = 0;

        }

        //private Collection<CacheRegion> GetRegionResult(IEnumerable<PSObject> r)
        //{
        //    var result = new Collection<CacheRegion>();

        //    foreach (var region in r)
        //    {
        //        result.Add( new CacheRegion
        //                        (
        //                           (string)region.Properties["RegionName"].Value,
        //                           (string)region.Properties["Role"].Value ,
        //                           (int)region.Properties["PortNo"].Value ,
        //                           this
        //                        ) );
        //    }
        //    return result;
        //}

        /// <summary>
        /// Add Region to cache name.
        /// </summary>
        /// <param name="region">Region Name</param>
        /// <param name="evictionOn">Eviction On</param>
        public void AddRegion( string region , bool evictionOn )
        {
            try
            {
                var client = CacheClientFactory.GetCache(this);

                if ( client != null )
                {
                    Log(new PowerShellCommand { CommandName = string.Format(@"DataCache.CreateRegion( {0} )", region) });
                    client.CreateRegion(region);
                }
            }
            catch ( Exception ex)
            {
                Log( new PowerShellCommand { CommandName = ex.Message } );
            }
            LoadRegions();
        }

        /// <summary>
        /// Load statistics of a named cache
        /// </summary>
        public override CacheStats LoadStatistics()
        {
            using (var session = SessionFactory.getLocalSession())
            {
                Parent.Cluster.UseCacheCluster(session);

                var r = Invoke(session, "Get-CacheStatistics", Params);
                if (r != null)
                {
                    Collection<NamedCacheStats> result = r.BuildNamedCacheStats();

                    if (result.Count > 0)
                        NamedCacheStats = result[0];
                }
                else
                    NamedCacheStats = new NamedCacheStats();

                return NamedCacheStats;
            }
        }

        /// <summary>
        /// Returns the configuration details for the named cache
        /// </summary>
        /// <returns>Returns the configuration details for the named cache</returns>
        public override void LoadConfig()
        {
            try
            {
                using (var session = SessionFactory.getLocalSession())
                {
                    Parent.Cluster.UseCacheCluster(session);
                    LoadConfig(session);
                }
            }
            catch ( Exception ex )
            {
                AddResultToLog( ex.Message );
            }
        }

        /// <summary>
        /// Returns the configuration details for the named cache
        /// </summary>
        /// <returns>Returns the configuration details for the named cache</returns>
        protected void LoadConfig(ISession session)
        {
            try
            {
                var r = Invoke(session, "Get-CacheConfig", Params);
                if (r != null && r.Count > 0)
                {
                    var result = r[0];
                    if (result != null)
                    {
                        Name = Convert.ToString(result.Properties["CacheName"].Value);
                        Eviction = (Eviction)Enum.Parse(typeof(Eviction), result.Properties["EvictionType"].Value.ToString());
                        IsExpirable = Convert.ToBoolean(result.Properties["IsExpirable"].Value);
                        NotificationsEnabled = Convert.ToBoolean(result.Properties["NotificationsEnabled"].Value);
                        TTL = Convert.ToInt64(result.Properties["TimeToLive"].Value);
                        IsHighAvailability = Convert.ToBoolean(result.Properties["Secondaries"].Value);
                        CacheType = Convert.ToString(result.Properties["CacheType"].Value);
                    }
                }
            }
            catch (Exception ex)
            {
                AddResultToLog(ex.Message);
            }
        }

        /// <summary>
        /// Updates the configuration settings of the named cache
        /// </summary>
        public override void SaveConfig()
        {
            var p = new Dictionary< string , object >
                        {
                            {"CacheName"    , Name},
                            {"Secondaries"  , IsHighAvailability ? 1 : 0 },
                            {"TimeToLive"   , TTL},
                            {"NotificationsEnabled"  , NotificationsEnabled},
                            {"Eviction"     , Eviction},
                            {"Expirable"    , IsExpirable}
                        };
			if (IsHighAvailability)
				p.Add("Force", null);

            using (var session = SessionFactory.getLocalSession())
            {
                Parent.Cluster.UseCacheCluster(session);
				Parent.Cluster.Stop(session, false);
				var r = Invoke(session, "Set-CacheConfig", p);
				Parent.Cluster.Start(session);
            }
        }

        public override void Add( CacheEntityBase item )
        {
            if ( item is CacheRegion ) Add(item as CacheRegion);
        }
        public override void Add( CacheRegion region )
        {
           AddRegion( region.Name , region.EvictionOn);
        }
        public override void Remove( CacheRegion region )
        {
            Remove( region as CacheEntityBase );
        }
        public override void Remove( CacheEntityBase item )
        {
            try
            {
                Log( "DataCliet.RemoveRegion(...)" );
                var dc = CacheClientFactory.GetCache( this );
                if ( dc == null ) return;

                dc.RemoveRegion( item.Name );
            }
            catch ( Exception ex )
            {
                Log( new PowerShellCommand { CommandName = ex.Message } );
            }
            LoadRegions();
        }
        #endregion

        #region Not Implemented
        public override void Start()
        {
            throw new NotImplementedException();
        }
        public override void Stop()
        {
            throw new NotImplementedException();
        }
        public override void LoadChildren()
        {
            throw new NotImplementedException();
        }
        #endregion


		private Collection<CacheHost> _hosts = new Collection<CacheHost>();

		public Collection<CacheHost> Hosts
		{
			get { return _hosts; }
			set { _hosts = value; OnPropertyChanged("Hosts"); }
		}

		public ObservableCollection<Object> Children
		{
			get
			{
				List<Object> o = new List<Object>();
				o.AddRange(Hosts);
				//o.AddRange(CacheRegions);
				return new ObservableCollection<Object>(o);
			}
		} 
    }
}
